package com.hzy.sharding.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.google.common.collect.Lists;
import io.shardingsphere.api.algorithm.sharding.hint.HintShardingAlgorithm;
import io.shardingsphere.api.config.rule.ShardingRuleConfiguration;
import io.shardingsphere.api.config.rule.TableRuleConfiguration;
import io.shardingsphere.api.config.strategy.HintShardingStrategyConfiguration;
import io.shardingsphere.api.config.strategy.InlineShardingStrategyConfiguration;
import io.shardingsphere.api.config.strategy.NoneShardingStrategyConfiguration;
import io.shardingsphere.core.rule.ShardingRule;
import io.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

@Configuration
public class DataSourceConfig {

    @Bean(name = "data0")
    @ConfigurationProperties(prefix = "spring.datasource.data0")
    public DataSource dataSource0() {
        return new DruidDataSource();
    }

    @Bean(name = "data1")
    @ConfigurationProperties(prefix = "spring.datasource.data1")
    public DataSource dataSource1() {
        return new DruidDataSource();
    }

    @Bean(name = "dataSource")
    public DataSource dataSource(DataSource data0,DataSource data1) throws SQLException {
        Map<String, DataSource> dataSourceMap = new HashMap<>(); //设置分库映射
        dataSourceMap.put("data0", data0);
        dataSourceMap.put("data1", data1);

        //2、分库分表配置
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        //2.2、配置各个表的分库分表策略，这里只配了一张表的就是t_order
        shardingRuleConfig.getTableRuleConfigs().addAll(tableRuleConfigurations());
        //2.5、配置默认分表规则
//        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new NoneShardingStrategyConfiguration());
        //2.6、配置默认分库规则(不配置分库规则,则只采用分表规则)
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new HintShardingStrategyConfiguration(new DatasourceRoutingAlgorithm()));
        //2.7、配置默认数据源
        shardingRuleConfig.setDefaultDataSourceName("data0");


        Map<String, Object> dataSourceNames = new HashMap<>();
        dataSourceNames.put("data0", "data0");
        dataSourceNames.put("data1", "data1");

        DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, dataSourceNames, getProperties());
        return dataSource;
    }

    /**
     * 表规则设定
     * @return
     */
    public List<TableRuleConfiguration> tableRuleConfigurations(){
        List<TableRuleConfiguration> tableRuleConfigs = new LinkedList();
        List<String> tableNames = new ArrayList<>();
        tableNames.add("t_goods");
        tableNames.add("t_student");
        for (String table: tableNames){
            TableRuleConfiguration ruleConfiguration = new TableRuleConfiguration();
            String algorithmExpression = table.concat("_0");
            // 表策略
            ruleConfiguration.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("create_date", algorithmExpression));


            ruleConfiguration.setLogicTable("data0");

            tableRuleConfigs.add(ruleConfiguration);
        }
        return tableRuleConfigs;
    }

    /**
     * SqlSessionFactory
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setTypeAliasesPackage("com.hzy.sharding.entity");
        // 设置mapper.xml的位置路径
        Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
        factoryBean.setMapperLocations(resources);
        return factoryBean.getObject();
    }

    /**
     * 事务
     * @param dataSource
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * mybatis配置
     * @return
     */
    @Bean
    public MapperScannerConfigurer scannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        configurer.setBasePackage("com.hzy.sharding.dao");
        return configurer;
    }

    private static Properties getProperties() {
        Properties propertie = new Properties();
        //是否打印SQL解析和改写日志
        propertie.setProperty("sql.show",Boolean.TRUE.toString());
        //用于SQL执行的工作线程数量，为零则表示无限制
        propertie.setProperty("executor.size","4");
        //每个物理数据库为每次查询分配的最大连接数量
        propertie.setProperty("max.connections.size.per.query","1");
        //是否在启动时检查分表元数据一致性
        propertie.setProperty("check.table.metadata.enabled","false");
        return propertie;
    }
}
